<?php

namespace qywxmsgsdk\Provider;

use qywxmsgsdk\Exception\FinanceSDKException;

class ChatSdk
{
    private static      $lib     = null;
    private ?\FFI\CData $sdk     = null;
    private array       $options = [
        'proxy_host'     => "",
        'proxy_password' => "",
        'timeout'        => 10, // 默认超时时间为10s
    ];

    /**
     * string $corpId 企业号
     * string $secret 秘钥
     *
     * 可选参数
     * array $options = [
     *   'proxy_host' => string,
     *   'proxy_password' => string,
     *   'timeout' => 10, // 默认超时时间为10s
     * ]
     */
    public function __construct(string $corpId, string $secret, array $options = [])
    {
        if (!self::$lib) {
            $de64 = base64_decode('dHlwZWRlZiBzdHJ1Y3QgV2VXb3JrRmluYW5jZVNka190IFdlV29ya0ZpbmFuY2VTZGtfdDsNCg0KdHlwZWRlZiBzdHJ1Y3QgU2xpY2VfdCB7DQogICAgY2hhciogYnVmOw0KICAgIGludCBsZW47DQp9IFNsaWNlX3Q7DQoNCnR5cGVkZWYgc3RydWN0IE1lZGlhRGF0YSB7DQogICAgY2hhciogb3V0aW5kZXhidWY7DQogICAgaW50IG91dF9sZW47DQogICAgY2hhciogZGF0YTsNCiAgICBpbnQgZGF0YV9sZW47DQogICAgaW50IGlzX2ZpbmlzaDsNCn0gTWVkaWFEYXRhX3Q7DQoNCldlV29ya0ZpbmFuY2VTZGtfdCogTmV3U2RrKCk7DQoNCmludCBJbml0KFdlV29ya0ZpbmFuY2VTZGtfdCogc2RrLCBjb25zdCBjaGFyKiBjb3JwaWQsIGNvbnN0IGNoYXIqIHNlY3JldCk7DQoNCmludCBHZXRDaGF0RGF0YShXZVdvcmtGaW5hbmNlU2RrX3QqIHNkaywgdW5zaWduZWQgbG9uZyBsb25nIHNlcSwgdW5zaWduZWQgaW50IGxpbWl0LCBjb25zdCBjaGFyICpwcm94eSxjb25zdCBjaGFyKiBwYXNzd2QsIGludCB0aW1lb3V0LFNsaWNlX3QqIGNoYXREYXRhcyk7DQoNCmludCBEZWNyeXB0RGF0YShjb25zdCBjaGFyKiBlbmNyeXB0X2tleSwgY29uc3QgY2hhciogZW5jcnlwdF9tc2csIFNsaWNlX3QqIG1zZyk7DQoNCmludCBHZXRNZWRpYURhdGEoV2VXb3JrRmluYW5jZVNka190KiBzZGssIGNvbnN0IGNoYXIqIGluZGV4YnVmLA0KCQkJCQljb25zdCBjaGFyKiBzZGtGaWxlaWQsY29uc3QgY2hhciAqcHJveHksY29uc3QgY2hhciogcGFzc3dkLCBpbnQgdGltZW91dCwgTWVkaWFEYXRhX3QqIG1lZGlhX2RhdGEpOw0KDQp2b2lkIERlc3Ryb3lTZGsoV2VXb3JrRmluYW5jZVNka190KiBzZGspOw0KDQpTbGljZV90KiBOZXdTbGljZSgpOw0KDQp2b2lkIEZyZWVTbGljZShTbGljZV90KiBzbGljZSk7DQoNCmNoYXIqIEdldENvbnRlbnRGcm9tU2xpY2UoU2xpY2VfdCogc2xpY2UpOw0KaW50IEdldFNsaWNlTGVuKFNsaWNlX3QqIHNsaWNlKTsNCg0KTWVkaWFEYXRhX3QqICBOZXdNZWRpYURhdGEoKTsNCg0Kdm9pZCBGcmVlTWVkaWFEYXRhKE1lZGlhRGF0YV90KiBtZWRpYV9kYXRhKTsNCg0KY2hhciogR2V0T3V0SW5kZXhCdWYoTWVkaWFEYXRhX3QqIG1lZGlhX2RhdGEpOw0KY2hhciogR2V0RGF0YShNZWRpYURhdGFfdCogbWVkaWFfZGF0YSk7DQppbnQgR2V0SW5kZXhMZW4oTWVkaWFEYXRhX3QqIG1lZGlhX2RhdGEpOw0KaW50IEdldERhdGFMZW4oTWVkaWFEYXRhX3QqIG1lZGlhX2RhdGEpOw0KaW50IElzTWVkaWFEYXRhRmluaXNoKE1lZGlhRGF0YV90KiBtZWRpYV9kYXRhKTs');
            $pathdir = __DIR__ . DIRECTORY_SEPARATOR . 'libWeWorkFinanceSdk';
            self::$lib = \FFI::cdef($de64, $pathdir);
        }
        $this->sdk = self::$lib->NewSdk();
        $ret       = self::$lib->Init($this->sdk, $corpId, $secret);

        if ($ret != 0) {
            // sdk需要主动释放
            self::$lib->DestroySdk($this->sdk);
            $this->sdk = null;
            $msg       = sprintf("init sdk err ret: %d\n", $ret);
            throw new FinanceSDKException($msg);
        }

        $this->options = array_merge($this->options, $options);
    }

    public function __destruct()
    {
        // sdk需要主动释放
        if (!is_null($this->sdk)) {
            self::$lib->DestroySdk($this->sdk);
        }
    }

    /**
     * 获取消息内容
     * @param int $seq
     * @param int $limit
     * @return string
     * @throws FinanceSDKException
     */
    public function getChatData(int $seq = 0, int $limit = 10): string
    {
        $chatDatas = self::$lib->NewSlice();
        $ret       = self::$lib->GetChatData($this->sdk, $seq, $limit,
            $this->options['proxy_host'],
            $this->options['proxy_password'],
            $this->options['timeout'],
            $chatDatas);
        if ($ret != 0) {
            self::$lib->FreeSlice($chatDatas);
            $msg = sprintf("GetChatData err ret:%d\n", $ret);
            throw new FinanceSDKException($msg);
        }
        $data = \FFI::string(self::$lib->GetContentFromSlice($chatDatas), self::$lib->GetSliceLen($chatDatas));
        self::$lib->FreeSlice($chatDatas);
        return $data;
    }

    /**
     * 下载资源
     * @param string $sdkfileid
     * @param string $saveTo
     * @return bool
     * @throws FinanceSDKException
     */
    public function downloadMedia(string $sdkfileid, string $saveTo): bool
    {
        $mediaDatas = self::$lib->NewMediaData();
        $fp         = fopen($saveTo, "wb");
        do {
            $ret = self::$lib->GetMediaData($this->sdk, self::$lib->GetOutIndexBuf($mediaDatas),
                $sdkfileid,
                $this->options['proxy_host'],
                $this->options['proxy_password'],
                $this->options['timeout'],
                $mediaDatas
            );
            if (0 != $ret) {
                self::$lib->FreeMediaData($mediaDatas);
                fclose($fp) && unlink($saveTo);
                $msg = sprintf("sdk get media data err, ret: %d\n", $ret);
                throw new FinanceSDKException($msg);
            }
            // FILE_APPEND
            $data = \FFI::string(self::$lib->GetData($mediaDatas), self::$lib->GetDataLen($mediaDatas));
            fwrite($fp, $data, self::$lib->GetDataLen($mediaDatas));
        } while (self::$lib->IsMediaDataFinish($mediaDatas) == 0);
        fclose($fp);
        self::$lib->FreeMediaData($mediaDatas);
        return true;
    }

    /**
     * 拉取静态资源数据，用于可以支持追加模式的三方存储平台
     *
     * 返回的数据结构体
     * $ret = [
     *  'data' => '' // string 返回的数据
     *  'nextIndex' => 'ddd' // string 获取下一段数据的句柄
     *  'isFinished' => int // 1 数据已拉取完毕
     * ];
     * @param string $sdkfileid
     * @param string $indexBuf
     * @return array
     * @throws FinanceSDKException
     */
    public function getMediaData(string $sdkfileid, string $indexBuf = ""): array
    {
        $mediaDatas = self::$lib->NewMediaData();
        $ret        = self::$lib->GetMediaData($this->sdk, $indexBuf, $sdkfileid,
            $this->options['proxy_host'],
            $this->options['proxy_password'],
            $this->options['timeout'],
            $mediaDatas
        );
        if (0 != $ret) {
            self::$lib->FreeMediaData($mediaDatas);
            $msg = sprintf("sdk get media data err, ret: %d\n", $ret);
            throw new FinanceSDKException($msg);
        }
        $data       = \FFI::string(self::$lib->GetData($mediaDatas), self::$lib->GetDataLen($mediaDatas));
        $nextIndex  = self::$lib->GetOutIndexBuf($mediaDatas);
        $isFinished = self::$lib->IsMediaDataFinish($mediaDatas) == 1 ?: 0;
        self::$lib->FreeMediaData($mediaDatas);
        return [
            "data"       => $data,
            "nextIndex"  => \FFI::string($nextIndex),
            "isFinished" => $isFinished,
        ];
    }

    /**
     * 解密数据
     *  $randomKey 通过openssl解密后的key
     *  $encryptStr chats 的加密数据
     * @param string $randomKey
     * @param string $encryptStr
     * @return string
     */
    public function decryptData(string $randomKey, string $encryptStr): string
    {
        $chatDatas = self::$lib->NewSlice();
        $ret       = self::$lib->DecryptData($randomKey, $encryptStr, $chatDatas);//0=解密成功
        $data      = \FFI::string(self::$lib->GetContentFromSlice($chatDatas), self::$lib->GetSliceLen($chatDatas));
        self::$lib->FreeSlice($chatDatas);
        return $data;
    }
}
